package com.mrd.modules.redis.config;

import java.net.UnknownHostException;
import java.util.ArrayList;
import java.util.List;

import org.apache.commons.pool2.impl.GenericObjectPool;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingClass;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.data.redis.connection.RedisNode;
import org.springframework.data.redis.connection.RedisSentinelConfiguration;
import org.springframework.data.redis.connection.jedis.JedisConnection;
import org.springframework.data.redis.connection.jedis.JedisConnectionFactory;
import org.springframework.data.redis.core.RedisOperations;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.data.redis.serializer.GenericJackson2JsonRedisSerializer;
import org.springframework.data.redis.serializer.StringRedisSerializer;
import org.springframework.util.Assert;
import org.springframework.util.StringUtils;

import com.mrd.modules.redis.config.RedisProperties.Pool;
import com.mrd.modules.redis.config.RedisProperties.Sentinel;

import redis.clients.jedis.Jedis;
import redis.clients.jedis.JedisPoolConfig;

@Configuration
@ConditionalOnClass({JedisConnection.class, RedisOperations.class, Jedis.class})
@EnableConfigurationProperties
public class RedisAutoConfiguration {
  public RedisAutoConfiguration() {}

  @Bean(name = {"org.springframework.autoconfigure.redis.RedisProperties"})
  @ConditionalOnMissingBean
  public RedisProperties redisProperties() {
    return new RedisProperties();
  }

  @Configuration
  protected static class RedisConfiguration {
    protected RedisConfiguration() {}

    @Bean
    @ConditionalOnMissingBean(name = {"redisTemplate"})
    public RedisTemplate<Object, Object> redisTemplate(
        RedisConnectionFactory redisConnectionFactory) throws UnknownHostException {
      RedisTemplate template = new RedisTemplate();
      template.setConnectionFactory(redisConnectionFactory);
      template.setStringSerializer(new StringRedisSerializer());
      template.setDefaultSerializer(new GenericJackson2JsonRedisSerializer());
      template.setEnableDefaultSerializer(true);
      template.afterPropertiesSet();
      return template;
    }

    @Bean
    @ConditionalOnMissingBean({StringRedisTemplate.class})
    public StringRedisTemplate stringRedisTemplate(RedisConnectionFactory redisConnectionFactory)
        throws UnknownHostException {
      StringRedisTemplate template = new StringRedisTemplate();
      template.setConnectionFactory(redisConnectionFactory);
      return template;
    }
  }

  @Configuration
  @ConditionalOnClass({GenericObjectPool.class})
  protected static class RedisPooledConnectionConfiguration
      extends RedisAutoConfiguration.AbstractRedisConfiguration {
    protected RedisPooledConnectionConfiguration() {}

    @Bean
    @ConditionalOnMissingBean
    public RedisConnectionFactory redisConnectionFactory() throws UnknownHostException {
      return this.applyProperties(this.createJedisConnectionFactory());
    }

    private JedisConnectionFactory createJedisConnectionFactory() {
      return this.properties.getPool() != null
          ? new JedisConnectionFactory(this.getSentinelConfig(), this.jedisPoolConfig())
          : new JedisConnectionFactory(this.getSentinelConfig());
    }

    private JedisPoolConfig jedisPoolConfig() {
      JedisPoolConfig config = new JedisPoolConfig();
      Pool props = this.properties.getPool();
      config.setMaxTotal(props.getMaxActive());
      config.setMaxIdle(props.getMaxIdle());
      config.setMinIdle(props.getMinIdle());
      config.setMaxWaitMillis((long) props.getMaxWait());
      return config;
    }
  }

  @Configuration
  @ConditionalOnMissingClass({"org.apache.commons.pool2.impl.GenericObjectPool"})
  protected static class RedisConnectionConfiguration
      extends RedisAutoConfiguration.AbstractRedisConfiguration {
    protected RedisConnectionConfiguration() {}

    @Bean
    @ConditionalOnMissingBean
    public RedisConnectionFactory redisConnectionFactory() throws UnknownHostException {
      return this.applyProperties(new JedisConnectionFactory(this.getSentinelConfig()));
    }
  }

  protected abstract static class AbstractRedisConfiguration {
    @Autowired
    protected RedisProperties properties;
    @Autowired(required = false)
    private RedisSentinelConfiguration sentinelConfiguration;

    protected AbstractRedisConfiguration() {}

    protected final JedisConnectionFactory applyProperties(JedisConnectionFactory factory) {
      factory.setHostName(this.properties.getHost());
      factory.setPort(this.properties.getPort());
      if (this.properties.getPassword() != null) {
        factory.setPassword(this.properties.getPassword());
      }

      factory.setDatabase(this.properties.getDatabase());
      if (this.properties.getTimeout() > 0) {
        factory.setTimeout(this.properties.getTimeout());
      }

      return factory;
    }

    protected final RedisSentinelConfiguration getSentinelConfig() {
      if (this.sentinelConfiguration != null) {
        return this.sentinelConfiguration;
      } else {
        Sentinel sentinelProperties = this.properties.getSentinel();
        if (sentinelProperties != null) {
          RedisSentinelConfiguration config = new RedisSentinelConfiguration();
          config.master(sentinelProperties.getMaster());
          config.setSentinels(this.createSentinels(sentinelProperties));
          return config;
        } else {
          return null;
        }
      }
    }

    private List<RedisNode> createSentinels(Sentinel sentinel) {
      ArrayList sentinels = new ArrayList();
      String nodes = sentinel.getNodes();
      String[] var4 = StringUtils.commaDelimitedListToStringArray(nodes);
      int var5 = var4.length;

      for (int var6 = 0; var6 < var5; ++var6) {
        String node = var4[var6];

        try {
          String[] ex = StringUtils.split(node, ":");
          Assert.state(ex.length == 2, "Must be defined as \'host:port\'");
          sentinels.add(new RedisNode(ex[0], Integer.valueOf(ex[1]).intValue()));
        } catch (RuntimeException var9) {
          throw new IllegalStateException("Invalid redis sentinel property \'" + node + "\'", var9);
        }
      }

      return sentinels;
    }
  }
}

